package base;
import java.net.InetAddress;
import utilities.*;
import javax.persistence.*;

import java.sql.Timestamp;
import java.util.*;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

import com.sun.org.apache.bcel.internal.generic.GETSTATIC;

public class DataTracker {

	static private EntityManagerFactory emf;

	static {
		emf = Persistence.createEntityManagerFactory("Tracker");
	}

	private EntityManager em;
	private EntityTransaction transac;

	public static EntityManagerFactory getEmf() {
		return emf;
	}
	
	static public void init (){}
	
	public void createEm() {
		em = emf.createEntityManager();
		transac = em.getTransaction();
		transac.begin();
	}

	File addFile(File file) {
		Query q = em.createQuery("select f from File f where f.key='" + file.getKey()+ "'");
		File f;
		try{
			f = (File) q.getSingleResult();
			return f;
		}
		catch(NoResultException e){
			//this.em.merge(file);
			this.em.persist(file);
			return file;
		}
	}

	Peer addPeer(Peer peer) {
		Query q = em.createQuery("select p from Peer p where " + "p.peerPortNumber=" + peer.getPeerPortNumber() + " and  p.peerIP =:ip");
		q.setParameter("ip", peer.getPeerIP().getAddress());
		Peer p;
		try{
			p = (Peer) q.getSingleResult();
			return p;
		}
		catch(NoResultException e){
			this.em.persist(peer);
			return peer;
		}	
	}
	
	public void addSeeder(File file, Peer peer){
		File f = addFile(file);
		Peer p = addPeer(peer);
		Query q = em.createQuery("select s from Share s where s.file.fileID=" + f.getFileID() + "and s.peer.peerID=" + p.getPeerID());
		Share res;
		try{
		res = (Share) q.getSingleResult();
			if(res.getStat() != Share.Stat.SEEDER)
			res.setStat(Share.Stat.SEEDER);
		}
		catch(NoResultException e){
			// Share does not exist in the db
			res = new Share(Share.Stat.SEEDER, file.getFilenameTmp(), f, p);
			this.em.persist(res);
			p.addShare(res);
			f.addShare(res);
			this.em.flush();
		}
		
	}
	
	public void addLeecher(String key, Peer peer){
		Peer p = addPeer(peer);
		Query q = em.createQuery("select f from File f where f.key='" + key +"'");
		File f;
		try{
			f = (File) q.getSingleResult();
			Query r = em.createQuery("select s from Share s where s.file.fileID=" + f.getFileID() + "and s.peer.peerID=" + p.getPeerID());
			Share res;
			try{
				res = (Share) r.getSingleResult();
				if(res.getStat() != Share.Stat.LEECHER)
					res.setStat(Share.Stat.LEECHER);
			}
			catch(NoResultException e){
				res = new Share(Share.Stat.LEECHER, f.getFilenameTmp(),f, p);
				this.em.persist(res);
			}				
		}
		catch(NoResultException e){
			System.out.println("Peer "+ peer.getPeerID() + ": File with key" + key +" does not exist");
		}		
	}
	
	
//////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////                              LOOK COMMAND                              /////////////////                               
//////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	
	public String findFileByName(String request, String name) {
		if(request.isEmpty()){
			request = "select distinct f from File f inner join f.shares s where s.fileName= '"
						+ name + "'";
			return request;
		}
		else{
			request += " and s.fileName='" + name + "'";
			return request;
		}
	}
	
	public String findFileByLength(String request, int length, Criterion.Operator op){
		String o=" ";
		switch (op){
		case EQ:
			o="=";
		break;
		case LEQ:
			o="<=";
		break;
		case GEQ:
			o=">=";	
		break;
		case L:
			o="<";
		break;
		case G:
			o=">";
		default:
		}
		if(request.equals(""))
			return "select distinct f from File f inner join f.shares s where f.fileLength"+ o + length ;	
		else
			return request + " and f.fileLength"+ o + length;
	
	}
	
	public List<File> executeFile(String request){
		Query q= em.createQuery(request);
		return q.getResultList();
	}
	
	public String responseLook(List<File> files){
		String sresponse = "list [";
		for(File f:files){
			String tmp = getFileNameByKey(f);
			sresponse += "" + tmp + " " + f.getFileLength() + " "+ f.getFilePieceSize()+ " " + f.getKey(); 
		}
		sresponse += "]";	
		
		return sresponse;
	}

	



	public String getFileNameByKey(File file){		
	  	Query q = em.createQuery("select s from Share s where s.file.key ='" + file.getKey() + "' and s.file.fileLength = " + file.getFileLength() + " and s.file.filePieceSize =" + file.getFilePieceSize());
		List<Share> res =  q.getResultList();
		return res.get(0).getFileName();
					
	}

	
	
//////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////                            GETFILE COMMAND                           ///////////////////                              
//////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	
	public List<Peer> findPeerByKey(String key) {
		Query q = em.createQuery("select p from Peer p inner join p.shares s where s.file.key='"
						+ key + "' and s.stat=0");
		List<Peer> peers = q.getResultList();
		return peers;
	}
		
	public void closeEm() {
		
		transac.commit();
		em.close();
	}

	static void disconnexionTrakerDB() {
		emf.close();
	}
	
	
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////                           UPDATE                           ////////////////////////                              
//////////////////////////////////////////////////////////////////////////////////////////////////////////
	
	public List<Peer> listPeers(){
		Query q = em.createQuery("select p from Peer p");
		return q.getResultList();
	}
	
	public void  deleteShares(Peer p){
		Query q = em.createQuery("delete from Share s where s.peer.peerID=" + p.getPeerID());
		q.executeUpdate();
	}
	
	public void deletePeer(Peer p){
		Query q = em.createQuery("delete from Peer p where p.peerID=" + p.getPeerID());
		q.executeUpdate();
	}
	
	public Peer findPeer(InetAddress ip){
		Query q = em.createQuery("select p from Peer p where  p.peerIP =:ip");
		q.setParameter("ip", ip.getAddress());
		try{
			return (Peer) q.getSingleResult();
		}
		catch(NoResultException e){
			System.out.println("The Peer with peerIP=" + ip + "does not exsist !!");
			return null;
		}
		catch(NonUniqueResultException e){
			System.out.println("There is more than one  Peer with peerIP=" + ip  + "!!");
			return null;			
		}
		
	}
	
	
	public void checkUpdate(Peer p, String key, Share.Stat stat){
		Query q = em.createQuery("select s from Share s where s.peer.peerID=" + p.getPeerID() + " and s.file.key='" + key + "'");
		Share s;
		try{
			s = (Share) q.getSingleResult();
			if(s.getStat() != stat)
				s.setStat(stat);
			p.setTime(new Timestamp(Calendar.getInstance().getTime().getTime()));
			//this.em.flush();
		}
		catch(NoResultException e){
			File f = findFileByKey(key);
			
			s = new Share(stat, f.getFilenameTmp(), f, p);
			this.em.persist(s);
			p.setTime(new Timestamp(Calendar.getInstance().getTime().getTime()));
			p.addShare(s);
			f.addShare(s);
			
			this.em.flush();			
		}
	}
	
	public File findFileByKey(String key){
		Query q = em.createQuery("select f from File f where f.key=" + "'" + key + "'");
		try{
			return (File) q.getSingleResult();
		}
		catch(NoResultException e){
			System.out.println("The file with the key "+ key + "does not exist !! ");
			return null;
		}		
	}
	
	
	public void printFiles(){
		
	Query query = em.createQuery("select f from File f");
    	List<File> files = query.getResultList();   
    	for(File f:files)
    		System.out.println(f);
	}		
	
	public void printShares(){
	Query query3 = em.createQuery("select s from Share s");           
    List<Share> shares = query3.getResultList();     
    for(Share s: shares)
    	System.out.println(s);
	}
	
	public void printPeers(){
		
	Query query2 = em.createQuery("select p from Peer p");           
	List<Peer> peers = query2.getResultList();
	for(Peer p: peers)
    	System.out.println(p); 
	}
	
	
	
}
